﻿using System;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using SecretLabs.NETMF.Hardware;
using SecretLabs.NETMF.Hardware.Netduino;
using System.IO.Ports;
using System.Text;
using System.Diagnostics;
using System.Collections;

namespace GrandPrix_Timer
{
    /* Grand Prix Timer V0.1************
     * Written by Jarrod Sinclair 
     * Created Jan, 04 2011
     * Updated Mar 02, 12 2011
     * Copyright © Jarrod Sinclair 2011
     * This Source Code is released for non-profit use only. 
     * For all other uses please contact me: jarrodsinclair@hotmail.com
     * 
     * NOTE: Although pinout for SERVO is in this code, the code to control the Server has not yet been added
     * 
     * Pins used by this program as it was written:
     * A0 = Lane_1
     * A1 = Lane_2
     * A2 = Lane_3
     * A3 = Lane_4
     * A4 = Lane_5
     * A5 = Lane_6
     * D6 = Lane_7
     * D5 = Lane_8
     * 
     * D0 COM1 RX = FTDI TX
     * D1 COM1 TX = FTDI RX
     * 
     * D2 = N/A
     * D12 = N/A
     * 
     * D10 = Start Gate Servo
     * D4 = Start Gate Switch
     * 
     * ONBOARD_LED = timer state. If LED is on, timer is running.
     *      
     * Pins for the Nokia 5110 LCD:
     * LCD|Netduino
     *   1|3V3
     *   2|GND
     *   3|D3 - ChipSelect (Pin 3)
     *   4|D7 - Reset(Pin 7)
     *   5|D8 - Data/Command (Pin 8)
     *   6|D11- SPI(MOSI)
     *   7|D12- SPI(SPCK)
     *   8|D9 - PWM (Backlight)
     * 
     ********************************/

    public class Program
    {
        
        #region Declare Statements
        // will be used to mask lanes
        static bool Lane_1_Enabled;
        static bool Lane_2_Enabled;
        static bool Lane_3_Enabled;
        static bool Lane_4_Enabled;
        static bool Lane_5_Enabled;
        static bool Lane_6_Enabled;
        static bool Lane_7_Enabled;
        static bool Lane_8_Enabled;

        // used to store the time the race started
        static DateTime StartTime;

        // used to store the maximium number of seconds for a race
        const int maxTime = 9;

        //State information for the Track and the Status Check function
        const int SG_OPEN = 1;
        const int SG_CLOSED = 0;
        const int LANE_1 = 2;
        const int LANE_2 = 3;
        const int LANE_3 = 4;
        const int LANE_4 = 5;
        const int LANE_5 = 6;
        const int LANE_6 = 7;
        const int LANE_7 = 8;
        const int LANE_8 = 9;
        const int SC_START = 99;
        const int SC_ERROR = 100;

        //flag for indicating if a status check is being called and the current state of status check
        static bool StatusCheck = false;
        static int SC_State = 0;
        static int SC_PrevState = 0;

        // the number of digits to have after the decimal in the lane times
        const int timerResolution = 4;
        static string timerRes = "";
        
        // used to record the time for each lane
        static string Lane_1_Time;
        static string Lane_2_Time;
        static string Lane_3_Time;
        static string Lane_4_Time;
        static string Lane_5_Time;
        static string Lane_6_Time;
        static string Lane_7_Time;
        static string Lane_8_Time;

        // used to record the time for each lane from the previous race
        static ArrayList FinishTimes = new ArrayList();  

        //setup GPIO ports
        static InterruptPort Lane_1 = new InterruptPort(Pins.GPIO_PIN_A0, false, Port.ResistorMode.Disabled, Port.InterruptMode.InterruptEdgeLevelHigh);
        static InterruptPort Lane_2 = new InterruptPort(Pins.GPIO_PIN_A1, false, Port.ResistorMode.Disabled, Port.InterruptMode.InterruptEdgeLevelHigh);
        static InterruptPort Lane_3 = new InterruptPort(Pins.GPIO_PIN_A2, false, Port.ResistorMode.Disabled, Port.InterruptMode.InterruptEdgeLevelHigh);
        static InterruptPort Lane_4 = new InterruptPort(Pins.GPIO_PIN_A3, false, Port.ResistorMode.Disabled, Port.InterruptMode.InterruptEdgeLevelHigh);
        static InterruptPort Lane_5 = new InterruptPort(Pins.GPIO_PIN_A4, false, Port.ResistorMode.Disabled, Port.InterruptMode.InterruptEdgeLevelHigh);
        static InterruptPort Lane_6 = new InterruptPort(Pins.GPIO_PIN_A5, false, Port.ResistorMode.Disabled, Port.InterruptMode.InterruptEdgeLevelHigh);
        static InterruptPort Lane_7 = new InterruptPort(Pins.GPIO_PIN_D6, false, Port.ResistorMode.Disabled, Port.InterruptMode.InterruptEdgeLevelHigh);
        static InterruptPort Lane_8 = new InterruptPort(Pins.GPIO_PIN_D5, false, Port.ResistorMode.Disabled, Port.InterruptMode.InterruptEdgeLevelHigh);
        static InterruptPort StartGate = new InterruptPort(Pins.GPIO_PIN_D4, true, Port.ResistorMode.Disabled, Port.InterruptMode.InterruptEdgeBoth);
        static OutputPort LED = new OutputPort(Pins.ONBOARD_LED, false);

        //static InterruptPort Onboard_SW = new InterruptPort(Pins.ONBOARD_SW1, false, Port.ResistorMode.Disabled, Port.InterruptMode.InterruptEdgeLow);
        
        //debug version - uncomment D9 for external switch
        static InputPort Button = new InputPort(Pins.ONBOARD_SW1, false, Port.ResistorMode.Disabled);
        //static InputPort Button = new InputPort(Pins.GPIO_PIN_D9, false, Port.ResistorMode.Disabled);

        //Setup Serial Port
        //This is the port we will use to recieve commands and send results back to the computer on
        static SerialPort COM1 = new SerialPort(SerialPorts.COM1, 9600, Parity.None, 8, StopBits.One);

        // tracks the state of the timer
        // true = race in progress
        // false = timer is off
        private static bool _timerOn;

        //used to control access to the serial port write
        static Object LockThis = new Object();

        /* Prepare LCD for display. NOTE: Code will still work without LCD Present
         * Pins for the LCD:
         * LCD|Netduino
         *   1|3V3
         *   2|GND
         *   3|ChipSelect (Pin 3)
         *   4|Reset(Pin 7)
         *   5|Data/Command (Pin 8)
         *   6|Pin 11
         *   7|Pin 13
         *   8|Backlight (Pin 9 PWM)
         */ 
        static Nokia_5110 LCD = new Nokia_5110();
        
        //object to pass to the Timer
        static Object xstate;

        static Timer RaceTimer = new Timer(RaceTimer_onFinish, xstate, Timeout.Infinite, Timeout.Infinite);

        #endregion Declare Statements

        static bool timerOn
        {
            get { return _timerOn; }
            set { 
                _timerOn = value;
                Debug.Print(value.ToString());
                if (value)
                {
                    //enable the timer
                    RaceTimer.Change((maxTime * 1000), Timeout.Infinite);
                    LED.Write(true);
                }
                else
                {
                    //disable the timer
                    RaceTimer.Change(Timeout.Infinite, Timeout.Infinite);
                    LED.Write(false);
                }
            }
        }

        public static void Main()
        {
            
            // which lanes will be used during the race, default to all in use
            Lane_1_Enabled = true;
            Lane_2_Enabled = true;
            Lane_3_Enabled = true;
            Lane_4_Enabled = true;
            Lane_5_Enabled = true;
            Lane_6_Enabled = true;
            Lane_7_Enabled = true;
            Lane_8_Enabled = true;

            for (int i = 1; i <= timerResolution; i++)
            {
                timerRes = timerRes + "9";
            }
            
            //setup the LCD
            LCD.Clear();
            LCD.SetCursor(0, 0);
            LCD.BacklightBrightness = 40;
            
            //create interrupt event handlers one for each GPIO setup.
            Lane_1.OnInterrupt += new NativeEventHandler(Lane_1_OnInterrupt);
            Lane_2.OnInterrupt += new NativeEventHandler(Lane_2_OnInterrupt);
            Lane_3.OnInterrupt += new NativeEventHandler(Lane_3_OnInterrupt);
            Lane_4.OnInterrupt += new NativeEventHandler(Lane_4_OnInterrupt);
            Lane_5.OnInterrupt += new NativeEventHandler(Lane_5_OnInterrupt);
            Lane_6.OnInterrupt += new NativeEventHandler(Lane_6_OnInterrupt);
            Lane_7.OnInterrupt += new NativeEventHandler(Lane_7_OnInterrupt);
            Lane_8.OnInterrupt += new NativeEventHandler(Lane_8_OnInterrupt);
            StartGate.OnInterrupt += new NativeEventHandler(StartGate_OnInterrupt);

            //write out startup text
            LCD.Clear();
            LCD.WriteText("Grand Prix Tim" +
                          "er v0.1  By:  " +
                          "JarrodSinclair" +
                          "Hold Button 4 " +
                          "for Status    " +
                          "Check         ");

            Thread.Sleep(1500);

            if (Button.Read() == false)
            {
                //button is being pressed during boot... call StatusCheck
                LCD.Clear();
                LCD.WriteText("STATUS CHECK: " +
                              "Be sure Start " +
                              "Gate is closed" +
                              "              " +
                              "Hold Button  " +
                              "to continue.  ");
                // let the user read the message
                Thread.Sleep(1000);
                
                while (Button.Read())
                {
                    // waiting for use to press button
                    Thread.Sleep(250);
                }
                StartStatusCheck();
            }

            LCD.Clear();
            LCD.WriteText("Grand Prix Tim" +
                          "er v0.1  By:  " +
                          "JarrodSinclair" +
                          "@Hotmail.com  " +
                          "              " +
                          "READY TO RACE ");

            COM1.DataReceived += new SerialDataReceivedEventHandler(SerialPort_DataRecieved);
            COM1.Open();

            //prepare the timer and lanes for a race
            Reset();
            
            while (true)
            {
                //read states and pump data to serial when ready.
                //Debug.Print("loop");
                // check to see if timer is on
                //if (timerOn)
                //{
                //    //if timer is enabled then verify that the maxTime has not elapsed
                //    if (DateTime.Now.Subtract(StartTime).Seconds >= maxTime)
                //    {
                //        // if maxTime hase been reached then stop the timer and display the results
                //        timerOn = false;
                //        if (true)
                //        {

                //        LCD.WriteText("1:" + Lane_1_Time + " 2:" + Lane_2_Time + " 3:" + Lane_3_Time + " 4:" + Lane_4_Time + " 5:" + Lane_5_Time + " 6:" + Lane_6_Time + " 7:" + Lane_7_Time + " 8:" + Lane_8_Time);
                //        }
                //    }
                //}
                //LED.Write(timerOn);
                Thread.Sleep(1000);
            }

        }
        
        // reset the system
        static void Reset()
        {
            // set the timer to off
            timerOn = false;

            //fill the StartTime with the minValue. This will be changed one the race starts
            StartTime = DateTime.MinValue;

            //delete the finish time previouisly recorded.
            //lock (FinishTimes)
            //{
            //    int theTotalNumberOfElement = FinishTimes.Count - 1;

            //    for (int counter = 0; counter < theTotalNumberOfElement; counter++)
            //    {
            //        FinishTimes.RemoveAt(counter); //or remove object you want: theArrayList.Remove(object);
            //    }
            //}
            FinishTimes.Clear();
            //foreach (var lane in FinishTimes)
            //{
            //    FinishTimes.Remove(lane);
            //}



            // set each lane to default time.
            Lane_1_Time = maxTime + "." + timerRes;
            Lane_2_Time = maxTime + "." + timerRes;
            Lane_3_Time = maxTime + "." + timerRes;
            Lane_4_Time = maxTime + "." + timerRes;
            Lane_5_Time = maxTime + "." + timerRes;
            Lane_6_Time = maxTime + "." + timerRes;
            Lane_7_Time = maxTime + "." + timerRes;
            Lane_8_Time = maxTime + "." + timerRes;

            //reset interrupts
            Lane_1.ClearInterrupt();
            Lane_2.ClearInterrupt();
            Lane_3.ClearInterrupt();
            Lane_4.ClearInterrupt();
            Lane_5.ClearInterrupt();
            Lane_6.ClearInterrupt();
            Lane_7.ClearInterrupt();
            Lane_8.ClearInterrupt();
            
            //Disable interrupts
            Lane_1.DisableInterrupt();
            Lane_2.DisableInterrupt();
            Lane_3.DisableInterrupt();
            Lane_4.DisableInterrupt();
            Lane_5.DisableInterrupt();
            Lane_6.DisableInterrupt();
            Lane_7.DisableInterrupt();
            Lane_8.DisableInterrupt();

            LCD.Clear();
            LCD.WriteText("Grand Prix Tim" +
                          "er v0.1  By:  " +
                          "JarrodSinclair" +
                          "@Hotmail.com  " +
                          "              " +
                          "READY TO RACE ");
            
        }

        // start the race
        static void startRace(DateTime StartTimeTicks)
        {
            // when servo is added commend out if statement
            //if (startGateClosed)
            //{
                // add code to trigger start gate servo here.
            timerOn = true;

            StartTime = StartTimeTicks;

            Lane_1.EnableInterrupt();
            Lane_2.EnableInterrupt();
            Lane_3.EnableInterrupt();
            Lane_4.EnableInterrupt();
            Lane_5.EnableInterrupt();
            Lane_6.EnableInterrupt();
            Lane_7.EnableInterrupt();
            Lane_8.EnableInterrupt();

            LCD.Clear();
            LCD.BacklightBrightness = 100;
            LCD.WriteText("Grand Prix Tim" +
                          "er v0.1       " +
                          "              " +
                          "Race Started! " +
                          "              " +
                          "              ");
                //return true;
            //}
            //else
            //{
                // the start gate is not closed error out
            //    return false;
            //}
        }

        static void SendRaceTimes(bool sendAll)
        {
            //Get the all the current race and send the over the serial port
            // sample: 1  5.381  2  6.101  3  5.344  4  5.720
            string maxT = maxTime + "." + timerRes;
            string RaceTimes = "";
            if (sendAll)
            {
                RaceTimes = " 1 " + Lane_1_Time;
                RaceTimes = RaceTimes + " 2 " + Lane_2_Time;
                RaceTimes = RaceTimes + " 3 " + Lane_3_Time;
                RaceTimes = RaceTimes + " 4 " + Lane_4_Time;
                RaceTimes = RaceTimes + " 5 " + Lane_5_Time;
                RaceTimes = RaceTimes + " 6 " + Lane_6_Time;
                RaceTimes = RaceTimes + " 7 " + Lane_7_Time;
                RaceTimes = RaceTimes + " 8 " + Lane_8_Time;
            }
            else
            {
                if (maxT == Lane_1_Time)
                    RaceTimes = " 1 " + Lane_1_Time + " ";
                if (maxT == Lane_2_Time)
                    RaceTimes = RaceTimes + " 2 " + Lane_2_Time + " ";
                if (maxT == Lane_3_Time)
                    RaceTimes = RaceTimes + " 3 " + Lane_3_Time + " ";
                if (maxT == Lane_4_Time)
                    RaceTimes = RaceTimes + " 4 " + Lane_4_Time + " ";
                if (maxT == Lane_5_Time)
                    RaceTimes = RaceTimes + " 5 " + Lane_5_Time + " ";
                if (maxT == Lane_6_Time)
                    RaceTimes = RaceTimes + " 6 " + Lane_6_Time + " ";
                if (maxT == Lane_7_Time)
                    RaceTimes = RaceTimes + " 7 " + Lane_7_Time + " ";
                if (maxT == Lane_8_Time)
                    RaceTimes = RaceTimes + " 8 " + Lane_8_Time + " ";
            }
            SerialPort_SendData(RaceTimes);
        }

        static void StartStatusCheck()
        {
            StatusCheck = true;

            //


            //enable all the lane interrupts 
            Lane_1.EnableInterrupt();
            Lane_2.EnableInterrupt();
            Lane_3.EnableInterrupt();
            Lane_4.EnableInterrupt();
            Lane_5.EnableInterrupt();
            Lane_6.EnableInterrupt();
            Lane_7.EnableInterrupt();
            Lane_8.EnableInterrupt();

            
            /* State Values
             * const int SG_OPEN = 0;
             * const int SG_CLOSED = 1;
             * const int LANE_1 = 2;
             * const int LANE_2 = 3;
             * const int LANE_3 = 4;
             * const int LANE_4 = 5;
             * const int LANE_5 = 6;
             * const int LANE_6 = 7;
             * const int LANE_7 = 8;
             * const int LANE_8 = 9;
             * const int SC_START = 99;
             * const int SC_ERROR = 100;
             */
            // #### Start Timer Status Check ####
            SC_State = SC_START;
            SC_PrevState = SC_START;
            
            // ask user to open the startgate and then wait for them to trigger it. 
            LCD.Clear();
            LCD.WriteText("STATUS CHECK: " +
                          "              " +
                          "              " +
                          "Open the Start" +
                          "gate          ");

            // wait for Start gate interrupt to set the StatusCheckState to SG_OPEN
            while (SC_State == SC_PrevState) { Thread.Sleep(250); }

            //##### Check to see that start gate was opened #####
            if (SC_State == SG_OPEN)
            {
                //success
                SC_PrevState = SC_State;
                
                // ask user to close the the start gate
                LCD.Clear();
                LCD.WriteText("STATUS CHECK: " +
                              "SUCCESS!!     " +
                              "              " +
                              "Close the     " +
                              "Start gate    ");
            }
            else
            { 
                //there was as problem
                LCD.Clear();
                LCD.WriteText("STATUS CHECK: " +
                              "ERROR!!       " +
                              "              " +
                              "Problem with: " +
                              SC_DecodeState(SC_State - SC_ERROR));
                Thread.Sleep(5000);
                return;
            }
            
            //Wait for Start gate interrupt to set the StatusCheckState to SG_CLOSED
            while (SC_State == SC_PrevState) { Thread.Sleep(250); }

            //##### Check that the Start Gate was Closed #####
            if (SC_State == SG_CLOSED)
            {
                //success
                SC_PrevState = SC_State;

                // ask user to user to trigger Lane 1 ONLY
                LCD.Clear();
                LCD.WriteText("STATUS CHECK: " +
                              "SUCCESS!!     " +
                              "              " +
                              "Trigger Lane 1" +
                              "     ONLY     ");
            }
            else
            {
                //there was as problem
                LCD.Clear();
                LCD.WriteText("STATUS CHECK: " +
                              "ERROR!!       " +
                              "              " +
                              "Problem with: " +
                              SC_DecodeState(SC_State - SC_ERROR));
                Thread.Sleep(5000);
                return;
            }
           
            // wait for Lane interrupt to set the StatusCheckState to LANE_1
            while (SC_State == SC_PrevState) { Thread.Sleep(250); }
            
            //##### CHECK that Lane 1 Was Triggered #####
            if (SC_State == LANE_1)
            {
                //success
                SC_PrevState = SC_State;

                // ask user to user to trigger Lane 2 ONLY
                LCD.Clear();
                LCD.WriteText("STATUS CHECK: " +
                              "SUCCESS!!     " +
                              "              " +
                              "Trigger Lane 2" +
                              "     ONLY     ");
            }
            else
            {
                //there was as problem
                LCD.Clear();
                LCD.WriteText("STATUS CHECK: " +
                              "ERROR!!       " +
                              "              " +
                              "Problem with: " +
                              SC_DecodeState(SC_State - SC_ERROR));
                Thread.Sleep(5000);
                return;
            }
            // wait for Lane interrupt to set the StatusCheckState to LANE_2
            while (SC_State == SC_PrevState) { Thread.Sleep(250); }

            //##### CHECK that Lane 2 was Triggered #####
            if (SC_State == LANE_2)
            {
                //success
                SC_PrevState = SC_State;

                // ask user to user to trigger Lane 3 ONLY
                LCD.Clear();
                LCD.WriteText("STATUS CHECK: " +
                              "SUCCESS!!     " +
                              "              " +
                              "Trigger Lane 3" +
                              "     ONLY     ");
            }
            else
            {
                //there was as problem
                LCD.Clear();
                LCD.WriteText("STATUS CHECK: " +
                              "ERROR!!       " +
                              "              " +
                              "Problem with: " +
                              SC_DecodeState(SC_State - SC_ERROR));
                Thread.Sleep(5000);
                return;
            }

            // wait for Lane interrupt to set the StatusCheckState to LANE_3
            while (SC_State == SC_PrevState) { Thread.Sleep(250); }

            //##### CHECK that Lane 3 was Triggered #####
            if (SC_State == LANE_3)
            {
                //success
                SC_PrevState = SC_State;

                // ask user to user to trigger Lane 4 ONLY
                LCD.Clear();
                LCD.WriteText("STATUS CHECK: " +
                              "SUCCESS!!     " +
                              "              " +
                              "Trigger Lane 4" +
                              "     ONLY     ");
            }
            else
            {
                //there was as problem
                LCD.WriteText("STATUS CHECK: " +
                              "ERROR!!       " +
                              "              " +
                              "Problem with: " +
                              SC_DecodeState(SC_State - SC_ERROR));
                Thread.Sleep(5000);
                return;
            }

            // wait for Lane interrupt to set the StatusCheckState to LANE_4
            while (SC_State == SC_PrevState) { Thread.Sleep(250); }

            //##### CHECK that Lane 4 was Triggered #####
            if (SC_State == LANE_4)
            {
                //success
                SC_PrevState = SC_State;

                // ask user to user to trigger Lane 5 ONLY
                LCD.Clear();
                LCD.WriteText("STATUS CHECK: " +
                              "SUCCESS!!     " +
                              "              " +
                              "Trigger Lane 5" +
                              "     ONLY     ");
            }
            else
            {
                //there was as problem
                LCD.Clear();
                LCD.WriteText("STATUS CHECK: " +
                              "ERROR!!       " +
                              "              " +
                              "Problem with: " +
                              SC_DecodeState(SC_State - SC_ERROR));
                Thread.Sleep(5000);
                return;
            }

            // wait for Lane interrupt to set the StatusCheckState to LANE_5
            while (SC_State == SC_PrevState) { Thread.Sleep(250); }

            //##### CHECK that Lane 5 was Triggered #####
            if (SC_State == LANE_5)
            {
                //success
                SC_PrevState = SC_State;

                // ask user to user to trigger Lane 6 ONLY
                LCD.Clear();
                LCD.WriteText("STATUS CHECK: " +
                              "SUCCESS!!     " +
                              "              " +
                              "Trigger Lane 6" +
                              "     ONLY     ");
            }
            else
            {
                //there was as problem
                LCD.Clear();
                LCD.WriteText("STATUS CHECK: " +
                              "ERROR!!       " +
                              "              " +
                              "Problem with: " +
                              SC_DecodeState(SC_State - SC_ERROR));
                Thread.Sleep(5000);
                return;
            }

            // wait for Lane interrupt to set the StatusCheckState to LANE_6
            while (SC_State == SC_PrevState) { Thread.Sleep(250); }

            //##### CHECK that Lane 6 was Triggered #####
            if (SC_State == LANE_6)
            {
                //success
                SC_PrevState = SC_State;

                // ask user to user to trigger Lane 7 ONLY
                LCD.Clear();
                LCD.WriteText("STATUS CHECK: " +
                              "SUCCESS!!     " +
                              "              " +
                              "Trigger Lane 7" +
                              "     ONLY     ");
            }
            else
            {
                //there was as problem
                LCD.Clear();
                LCD.WriteText("STATUS CHECK: " +
                              "ERROR!!       " +
                              "              " +
                              "Problem with: " +
                              SC_DecodeState(SC_State - SC_ERROR));
                Thread.Sleep(5000);
                return;
            }

            // wait for Lane interrupt to set the StatusCheckState to LANE_7
            while (SC_State == SC_PrevState) { Thread.Sleep(250); }

            //##### CHECK that Lane 7 was Triggered #####
            if (SC_State == LANE_7)
            {
                //success
                SC_PrevState = SC_State;

                // ask user to user to trigger Lane 8 ONLY
                LCD.Clear();
                LCD.WriteText("STATUS CHECK: " +
                              "SUCCESS!!     " +
                              "              " +
                              "Trigger Lane 8" +
                              "     ONLY     ");
            }
            else
            {
                //there was as problem
                LCD.Clear();
                LCD.WriteText("STATUS CHECK: " +
                              "ERROR!!       " +
                              "              " +
                              "Problem with: " +
                              SC_DecodeState(SC_State - SC_ERROR));
                Thread.Sleep(5000);
                return;
            }

            // wait for Lane interrupt to set the StatusCheckState to LANE_8
            while (SC_State == SC_PrevState) { Thread.Sleep(250); }

            //##### CHECK that Lane 8 was Triggered #####
            if (SC_State == LANE_8)
            {
                //success
                SC_PrevState = SC_State;

                //Status Check Complete
                LCD.Clear();
                LCD.WriteText("STATUS CHECK: " +
                              "SUCCESS!!     " +
                              "Testing       " +
                              "Complete,     " +
                              "Returning...  ");
            }
            else
            {
                //there was as problem
                LCD.Clear();
                LCD.WriteText("STATUS CHECK: " +
                              "ERROR!!       " +
                              "              " +
                              "Problem with: " +
                              SC_DecodeState(SC_State - SC_ERROR));
                Thread.Sleep(5000);
                return;
            }
            
            //disable all the lane interrupts 
            Lane_1.DisableInterrupt();
            Lane_2.DisableInterrupt();
            Lane_3.DisableInterrupt();
            Lane_4.DisableInterrupt();
            Lane_5.DisableInterrupt();
            Lane_6.DisableInterrupt();
            Lane_7.DisableInterrupt();
            Lane_8.DisableInterrupt();

            //Clear all the lane interrupts 
            Lane_1.ClearInterrupt();
            Lane_2.ClearInterrupt();
            Lane_3.ClearInterrupt();
            Lane_4.ClearInterrupt();
            Lane_5.ClearInterrupt();
            Lane_6.ClearInterrupt();
            Lane_7.ClearInterrupt();
            Lane_8.ClearInterrupt();

            StatusCheck = false;
        }

        static void FlashBacklight(int lenthOfSec)
        {
            for (int i = 1; i <= lenthOfSec; i++)
            {
                LCD.BacklightBrightness = 90;
                Thread.Sleep(500);
                LCD.BacklightBrightness = 40;
                Thread.Sleep(500);
            }
        }
        
        static bool VerifyInterrupts()
        {
            if (StartGate.Read() != false)
            {
                LCD.Clear();
                LCD.WriteText("STATUS CHECK: " +
                              "### ERROR! ###" +
                              "Start Gate is " +
                              "not ready     " +
                              "              " +
                              "### ERROR! ###");
                FlashBacklight(5);
                return false;
            }
            if (Lane_1.Read() != false)
            {
                LCD.Clear();
                LCD.WriteText("STATUS CHECK: " +
                              "### ERROR! ###" +
                              "Lane 1 is not " +
                              "ready         " +
                              "              " +
                              "### ERROR! ###");
                FlashBacklight(5);
                return false;
            }
            if (Lane_2.Read() != false)
            {
                LCD.Clear();
                LCD.WriteText("STATUS CHECK: " +
                              "### ERROR! ###" +
                              "Lane 2 is not " +
                              "ready         " +
                              "              " +
                              "### ERROR! ###");
                FlashBacklight(5);
                return false;
            }
            if (Lane_3.Read() != false)
            {
                LCD.Clear();
                LCD.WriteText("STATUS CHECK: " +
                              "### ERROR! ###" +
                              "Lane 3 is not " +
                              "ready         " +
                              "              " +
                              "### ERROR! ###");
                FlashBacklight(5);
                return false;
            }
            if (Lane_4.Read() != false)
            {
                LCD.Clear();
                LCD.WriteText("STATUS CHECK: " +
                              "### ERROR! ###" +
                              "Lane 4 is not " +
                              "ready         " +
                              "              " +
                              "### ERROR! ###");
                FlashBacklight(5);
                return false;
            }
            if (Lane_5.Read() != false)
            {
                LCD.Clear();
                LCD.WriteText("STATUS CHECK: " +
                              "### ERROR! ###" +
                              "Lane 5 is not " +
                              "ready         " +
                              "              " +
                              "### ERROR! ###");
                FlashBacklight(5);
                return false;
            }
            if (Lane_6.Read() != false)
            {
                LCD.Clear();
                LCD.WriteText("STATUS CHECK: " +
                              "### ERROR! ###" +
                              "Lane 6 is not " +
                              "ready         " +
                              "              " +
                              "### ERROR! ###");
                FlashBacklight(5);
                return false;
            }
            if (Lane_7.Read() != false)
            {
                LCD.Clear();
                LCD.WriteText("STATUS CHECK: " +
                              "### ERROR! ###" +
                              "Lane 7 is not " +
                              "ready         " +
                              "              " +
                              "### ERROR! ###");
                FlashBacklight(5);
                return false;
            }
            if (Lane_8.Read() != false)
            {
                LCD.Clear();
                LCD.WriteText("STATUS CHECK: " +
                              "### ERROR! ###" +
                              "Lane 8 is not " +
                              "ready         " +
                              "              " +
                              "### ERROR! ###");
                FlashBacklight(5);
                return false;
            }
            LCD.WriteText("STATUS CHECK: " +
                          "## SUCCESS! ##" +
                          "Startgate and " +
                          "all Lanes ok! " +
                          "              " +
                          "## SUCCESS! ##");
            return true;
        }

        static string SC_DecodeState(int SC_State)
        {
            /* State Values
             * const int SG_OPEN = 0;
             * const int SG_CLOSED = 1;
             * const int LANE_1 = 2;
             * const int LANE_2 = 3;
             * const int LANE_3 = 4;
             * const int LANE_4 = 5;
             * const int LANE_5 = 6;
             * const int LANE_6 = 7;
             * const int LANE_7 = 8;
             * const int LANE_8 = 9;
             * const int SC_START = 99;
             * const int SC_ERROR = 100;
             */
            switch (SC_State)
            {
                case SC_START:
                    return " SC_START     ";
                case SG_OPEN:
                    return " SG_OPEN      ";
                case SG_CLOSED:
                    return " SG_CLOSED    ";
                case LANE_1:
                    return " LANE_1       ";
                case LANE_2:
                    return " LANE_2       ";
                case LANE_3:
                    return " LANE_3       ";
                case LANE_4:
                    return " LANE_4       ";
                case LANE_5:
                    return " LANE_5       ";
                case LANE_6:
                    return " LANE_6       ";
                case LANE_7:
                    return " LANE_7       ";
                case LANE_8:
                    return " LANE_8       ";
            }
            return "UNKNOWN ERROR ";
        }

        static void ServoTheStarGate()
        {
            //TODO: Implement the Servo code that will open the start gate and start the race.
        }

        static void ProcessSerialCommand(string Command)
        {

            /* Commands to Process:
             * "R" - reset/ready
             * "F" - force elapsed times to be returned immediately for any lane that has finished before race is completed. 
             *       Unfinished lanes are returned as MaxTime plus .9 up to the TimerResolution. Default is 9.9999
             * "A" - return elapsed times again (if needed.) Normally the controller sends the race results after all four 
             *       lanes are finished, or time out, or are otherwise accounted for (when one or more lanes are masked.)
             * "C" - check track status. Depending on what checks were selected at compile time, either finish line 
             *       phototransistors, or both finish phototransistors and start gate status are checked. If a finish 
             *       line phototransistor is blocked it returns "NG" to the computer (unless it was masked.) If the start 
             *       gate it down, it returns "NG" to the computer.
             * "T" - trigger the start gate to drop. NOTE only works if survo is connected
             * "M1" - (or M2...M8) to mask an individual lane.
             * "X" - unmask all lanes.
             */
            
            if (Command.Length == 1) 

            {
                // Process for a Single Digit command listed above
                switch (Command)
                {
                    case "R":
                        // reset the device and prepare to race. stop the timer if nessary
                        Reset();
                        LCD.Clear();
                        LCD.WriteText("Serial command" +
                                      "Recieved      " +
                                      "              " +
                                      "RESET complete" +
                                      "READY TO RACE ");
                        break;
                    case "F":
                        SendRaceTimes(true);
                        LCD.Clear();
                        LCD.WriteText("Serial command" +
                                      "Recieved      " +
                                      "              " +
                                      "Race Times    " +
                                      "Sent          ");
                        break;
                    case "A":
                        SendRaceTimes(true);
                        LCD.Clear();
                        LCD.WriteText("Serial command" +
                                      "Recieved      " +
                                      "              " +
                                      "Race Times    " +
                                      "Sent          ");
                        break;
                    case "C":
                        if (VerifyInterrupts() == false)
                            SerialPort_SendData("NG");
                        break;
                    case "T":
                        ServoTheStarGate();
                        LCD.Clear();
                        LCD.WriteText("Serial command" +
                                      "Recieved      " +
                                      "              " +
                                      "Triggering the" +
                                      "StartGate     ");
                        break;
                    case "X":
                        Lane_1_Enabled = true;
                        Lane_2_Enabled = true;
                        Lane_3_Enabled = true;
                        Lane_4_Enabled = true;
                        Lane_5_Enabled = true;
                        Lane_6_Enabled = true;
                        Lane_7_Enabled = true;
                        Lane_8_Enabled = true;
                        LCD.Clear();
                        LCD.WriteText("Serial command" +
                                      "Recieved      " +
                                      "              " +
                                      "Lane Masking  " +
                                      "Removed       ");
                        break;
                    default:
                        // you should not be here, print error on LCD
                        Debug.Print(Command);
                        break;
                }
            }
            else if (Command.Length == 2)
            {
                /* Process the Command for lane masking */
                switch (Command)
                {
                    // because the command length was 2 characters we assume it was a lane masking request. If it was not then we ignore the command but let the user know we recieved it. 
                    case "M1":
                        Lane_1_Enabled = false;
                        LCD.Clear();
                        LCD.WriteText("Serial command" +
                                      "Recieved      " +
                                      "              " +
                                      "Lane 1 Masked " +
                                      "              ");
                        break;
                    case "M2":
                        Lane_2_Enabled = false;
                        LCD.Clear();
                        LCD.WriteText("Serial command" +
                                      "Recieved      " +
                                      "              " +
                                      "Lane 2 Masked " +
                                      "              ");
                        break;
                    case "M3":
                        Lane_3_Enabled = false;
                        LCD.Clear();
                        LCD.WriteText("Serial command" +
                                      "Recieved      " +
                                      "              " +
                                      "Lane 3 Masked " +
                                      "              ");
                        break;
                    case "M4":
                        Lane_4_Enabled = false;
                        LCD.Clear();
                        LCD.WriteText("Serial command" +
                                      "Recieved      " +
                                      "              " +
                                      "Lane 4 Masked " +
                                      "              ");
                        break;
                    case "M5":
                        Lane_5_Enabled = false;
                        LCD.Clear();
                        LCD.WriteText("Serial command" +
                                      "Recieved      " +
                                      "              " +
                                      "Lane 5 Masked " +
                                      "              ");
                        break;
                    case "M6":
                        Lane_6_Enabled = false;
                        LCD.Clear();
                        LCD.WriteText("Serial command" +
                                      "Recieved      " +
                                      "              " +
                                      "Lane 6 Masked " +
                                      "              ");
                        break;
                    case "M7":
                        Lane_7_Enabled = false;
                        LCD.Clear();
                        LCD.WriteText("Serial command" +
                                      "Recieved      " +
                                      "              " +
                                      "Lane 7 Masked " +
                                      "              ");
                        break;
                    case "M8":
                        Lane_8_Enabled = false;
                        LCD.Clear();
                        LCD.WriteText("Serial command" +
                                      "Recieved      " +
                                      "              " +
                                      "Lane 8 Masked " +
                                      "              ");
                        break;
                    default:
                        // you should not be here, print error on LCD
                        Debug.Print(Command);
                        LCD.Clear();
                        LCD.WriteText("Serial command" +
                                      "Recieved      " +
                                      "UNKNOWN CMD   " +
                                      "length = 2    " +
                                      "|" + Command + "|");
                        break;
                }
            }
            else
            {
                // houston... we have a problem ... but we are going to ignore it
                // you should not be here, print error on LCD
                Debug.Print(Command);
                LCD.Clear();
                LCD.WriteText("Serial Command" +
                              "Unknown L=" + Command.Length.ToString()  +
                              "|" + Command + "|");

            }

        }

        static void SerialPort_SendData(string data)
        {
            // make sure that only one thread (interrupt) is writing to the serial port at a time
            lock (LockThis)
            {
                byte[] sendData = Encoding.UTF8.GetBytes(data);
                COM1.Write(sendData, 0, sendData.Length);
            }
        }

        static void Lane_1_OnInterrupt(uint data1, uint data2, DateTime time)
        {
            Debug.Print("Lane_1 was triggered: " + time.Subtract(StartTime).Ticks.ToString());
            
            if (StatusCheck)
                    {
                        if (SC_State == SG_CLOSED)
                        {
                            // correct event was triggered
                            SC_State = LANE_1;
                            return;
                        }
                        else
                        {
                            // wrong event was triggered.
                            SC_State = SC_ERROR + LANE_1;
                            return;

                        }
                    }
            else if (timerOn & Lane_1_Enabled & !StatusCheck & Lane_1_Time == "9.9999")
            {
                int seconds;
                string ticksElapsed;
                
                // get the number of seconds that have elapsed
                seconds = time.Subtract(StartTime).Seconds;
                //check to make sure that elapsed time is less then max time
                if (seconds <= maxTime)
                {
                    // get the number of ticks that have elapsed
                    ticksElapsed = time.Subtract(StartTime).Ticks.ToString();

                    // build the time for this lane
                    Lane_1_Time = seconds.ToString() + "." + ticksElapsed.Substring(seconds.ToString().Length, timerResolution);
                    FinishTimes.Add("1:" + Lane_1_Time);
                    Debug.Print("Lane_1_Time:" + Lane_1_Time);

                    //write finish time to serial port.
                    SerialPort_SendData(" 1 " + Lane_1_Time);

                } 
            }
        }

        static void Lane_2_OnInterrupt(uint data1, uint data2, DateTime time)
        {
            Debug.Print("Lane_2 was triggered: " + time.Subtract(StartTime).Ticks.ToString());

            if (StatusCheck)
            {
                if (SC_State == LANE_1)
                {
                    // correct event was triggered
                    SC_State = LANE_2;
                    return;
                }
                else
                {
                    // wrong event was triggered.
                    SC_State = SC_ERROR + LANE_2;
                    return;

                }
            }
            else if (timerOn & Lane_2_Enabled & !StatusCheck & Lane_2_Time == "9.9999")
            {
                int seconds;
                string ticksElapsed;

                // get the number of seconds that have elapsed
                seconds = time.Subtract(StartTime).Seconds;
                //check to make sure that elapsed time is less then max time
                if (seconds <= maxTime)
                {
                    // get the number of ticks that have elapsed
                    ticksElapsed = time.Subtract(StartTime).Ticks.ToString();

                    // build the time for this lane
                    Lane_2_Time = seconds.ToString() + "." + ticksElapsed.Substring(seconds.ToString().Length, timerResolution);
                    FinishTimes.Add("2:" + Lane_2_Time);
                    Debug.Print("Lane_2_Time:" + Lane_2_Time);

                    //write finish time to serial port.
                    SerialPort_SendData(" 2 " + Lane_2_Time);

                }
            }
        }

        static void Lane_3_OnInterrupt(uint data1, uint data2, DateTime time)
        {
            Debug.Print("Lane_3 was triggered: " + time.Subtract(StartTime).Ticks.ToString());

            if (StatusCheck)
            {
                if (SC_State == LANE_2)
                {
                    // correct event was triggered
                    SC_State = LANE_3;
                    return;
                }
                else
                {
                    // wrong event was triggered.
                    SC_State = SC_ERROR + LANE_3;
                    return;
                }
            }
            else if (timerOn & Lane_3_Enabled & !StatusCheck & Lane_3_Time == "9.9999")
            {
                int seconds;
                string ticksElapsed;

                // get the number of seconds that have elapsed
                seconds = time.Subtract(StartTime).Seconds;
                //check to make sure that elapsed time is less then max time
                if (seconds <= maxTime)
                {
                    // get the number of ticks that have elapsed
                    ticksElapsed = time.Subtract(StartTime).Ticks.ToString();

                    // build the time for this lane
                    Lane_3_Time = seconds.ToString() + "." + ticksElapsed.Substring(seconds.ToString().Length, timerResolution);
                    FinishTimes.Add("3:" + Lane_3_Time);
                    Debug.Print("Lane_3_Time:" + Lane_3_Time);

                    //write finish time to serial port.
                    SerialPort_SendData(" 3 " + Lane_3_Time);
                }
            }
        }

        static void Lane_4_OnInterrupt(uint data1, uint data2, DateTime time)
        {
            Debug.Print("Lane_4 was triggered: " + time.Subtract(StartTime).Ticks.ToString());

            if (StatusCheck)
            {
                if (SC_State == LANE_3)
                {
                    // correct event was triggered
                    SC_State = LANE_4;
                    return;
                }
                else
                {
                    // wrong event was triggered.
                    SC_State = SC_ERROR + LANE_4;
                    return;
                }
            }
            else if (timerOn & Lane_4_Enabled & !StatusCheck & Lane_4_Time == "9.9999")
            {
                int seconds;
                string ticksElapsed;

                // get the number of seconds that have elapsed
                seconds = time.Subtract(StartTime).Seconds;
                //check to make sure that elapsed time is less then max time
                if (seconds <= maxTime)
                {
                    // get the number of ticks that have elapsed
                    ticksElapsed = time.Subtract(StartTime).Ticks.ToString();

                    // build the time for this lane
                    Lane_4_Time = seconds.ToString() + "." + ticksElapsed.Substring(seconds.ToString().Length, timerResolution);
                    FinishTimes.Add("4:" + Lane_4_Time);
                    Debug.Print("Lane_4_Time:" + Lane_4_Time);

                    //write finish time to serial port.
                    SerialPort_SendData(" 4 " + Lane_4_Time);
                }
            }
        }

        static void Lane_5_OnInterrupt(uint data1, uint data2, DateTime time)
        {
            Debug.Print("Lane_5 was triggered: " + time.Subtract(StartTime).Ticks.ToString());

            if (StatusCheck)
            {
                if (SC_State == LANE_4)
                {
                    // correct event was triggered
                    SC_State = LANE_5;
                    return;
                }
                else
                {
                    // wrong event was triggered.
                    SC_State = SC_ERROR + LANE_5;
                    return;
                }
            }
            else if (timerOn & Lane_5_Enabled & !StatusCheck & Lane_5_Time == "9.9999")
            {
                int seconds;
                string ticksElapsed;

                // get the number of seconds that have elapsed
                seconds = time.Subtract(StartTime).Seconds;
                //check to make sure that elapsed time is less then max time
                if (seconds <= maxTime)
                {
                    // get the number of ticks that have elapsed
                    ticksElapsed = time.Subtract(StartTime).Ticks.ToString();

                    // build the time for this lane
                    Lane_5_Time = seconds.ToString() + "." + ticksElapsed.Substring(seconds.ToString().Length, timerResolution);
                    FinishTimes.Add("5:" + Lane_5_Time);
                    Debug.Print("Lane_5_Time:" + Lane_5_Time);

                    //write finish time to serial port.
                    SerialPort_SendData(" 5 " + Lane_5_Time);
                }
            }
        }

        static void Lane_6_OnInterrupt(uint data1, uint data2, DateTime time)
        {
            Debug.Print("Lane_6 was triggered: " + time.Subtract(StartTime).Ticks.ToString());

            if (StatusCheck)
            {
                if (SC_State == LANE_5)
                {
                    // correct event was triggered
                    SC_State = LANE_6;
                    return;
                }
                else
                {
                    // wrong event was triggered.
                    SC_State = SC_ERROR + LANE_6;
                    return;
                }
            }
            else if (timerOn & Lane_6_Enabled & !StatusCheck & Lane_6_Time == "9.9999")
            {
                int seconds;
                string ticksElapsed;

                // get the number of seconds that have elapsed
                seconds = time.Subtract(StartTime).Seconds;
                //check to make sure that elapsed time is less then max time
                if (seconds <= maxTime)
                {
                    // get the number of ticks that have elapsed
                    ticksElapsed = time.Subtract(StartTime).Ticks.ToString();

                    // build the time for this lane
                    Lane_6_Time = seconds.ToString() + "." + ticksElapsed.Substring(seconds.ToString().Length, timerResolution);
                    FinishTimes.Add("6:" + Lane_6_Time);
                    Debug.Print("Lane_6_Time:" + Lane_6_Time);

                    //write finish time to serial port.
                    SerialPort_SendData(" 6 " + Lane_6_Time);
                }
            }
        }

        static void Lane_7_OnInterrupt(uint data1, uint data2, DateTime time)
        {
            Debug.Print("Lane_7 was triggered: " + time.Subtract(StartTime).Ticks.ToString());

            if (StatusCheck)
            {
                if (SC_State == LANE_6)
                {
                    // correct event was triggered
                    SC_State = LANE_7;
                    return;
                }
                else
                {
                    // wrong event was triggered.
                    SC_State = SC_ERROR + LANE_7;
                    return;
                }
            }
            else if (timerOn & Lane_7_Enabled & !StatusCheck & Lane_7_Time == "9.9999")
            {
                int seconds;
                string ticksElapsed;

                // get the number of seconds that have elapsed
                seconds = time.Subtract(StartTime).Seconds;
                //check to make sure that elapsed time is less then max time
                if (seconds <= maxTime)
                {
                    // get the number of ticks that have elapsed
                    ticksElapsed = time.Subtract(StartTime).Ticks.ToString();

                    // build the time for this lane
                    Lane_7_Time = seconds.ToString() + "." + ticksElapsed.Substring(seconds.ToString().Length, timerResolution);
                    FinishTimes.Add("7:" + Lane_7_Time);
                    Debug.Print("Lane_7_Time:" + Lane_7_Time);

                    //write finish time to serial port.
                    SerialPort_SendData(" 7 " + Lane_7_Time);
                }
            }
        }

        static void Lane_8_OnInterrupt(uint data1, uint data2, DateTime time)
        {
            Debug.Print("Lane_8 was triggered: " + time.Subtract(StartTime).Ticks.ToString());

            if (StatusCheck)
            {
                if (SC_State == LANE_7)
                {
                    // correct event was triggered
                    SC_State = LANE_8;
                    return;
                }
                else
                {
                    // wrong event was triggered.
                    SC_State = SC_ERROR + LANE_8;
                    return;
                }
            }
            else if (timerOn & Lane_8_Enabled & !StatusCheck & Lane_8_Time == "9.9999")
            {
                int seconds;
                string ticksElapsed;

                // get the number of seconds that have elapsed
                seconds = time.Subtract(StartTime).Seconds;
                //check to make sure that elapsed time is less then max time
                if (seconds <= maxTime)
                {
                    // get the number of ticks that have elapsed
                    ticksElapsed = time.Subtract(StartTime).Ticks.ToString();

                    // build the time for this lane
                    Lane_8_Time = seconds.ToString() + "." + ticksElapsed.Substring(seconds.ToString().Length, timerResolution);
                    FinishTimes.Add("8:" + Lane_8_Time);
                    Debug.Print("Lane_8_Time:" + Lane_8_Time);

                    //write finish time to serial port.
                    SerialPort_SendData(" 8 " + Lane_8_Time);
                }
            }
        }

        static void SerialPort_DataRecieved(object sender, SerialDataReceivedEventArgs e)
        {
            int bytesToRead = COM1.BytesToRead;
            byte[] recBytes = new byte[bytesToRead];
            COM1.Read(recBytes, 0, bytesToRead);
            //string SerialPortText = Encoding.UTF8.GetChars(recBytes).ToString();
            string SerialPortText = new String(System.Text.Encoding.UTF8.GetChars(recBytes));
            
            Debug.Print(SerialPortText);
            //M5\rM6\rM7\rCR
            if (SerialPortText.IndexOf("\r") > 0)
            {
                string[] multipleCommands = SerialPortText.Split("\r".ToCharArray());
                foreach (string Command in multipleCommands)
                {
                    if (Command != "")
                        ProcessSerialCommand(Command);
                    if (Command == "CR")
                    {
                        ProcessSerialCommand("C");
                        ProcessSerialCommand("R");
                    }
                }
            }
            else
            {
                ProcessSerialCommand(SerialPortText);
            }
        }

        static void RaceTimer_onFinish(Object state)
        {
            //Race timer has finished
            timerOn = false;

            LCD.Clear();
            string first, second, third;
            first = "";
            second = "";
            third = "";

            switch (FinishTimes.Count)
            {
                case 1:
                    first = (string) FinishTimes[0];
                    break;
                case 2:
                    first = (string) FinishTimes[0];
                    second = (string) FinishTimes[1];
                    break;
                case 3:
                    first = (string) FinishTimes[0];
                    second = (string) FinishTimes[1];
                    third = (string) FinishTimes[2];
                    break;
                case 4:
                    first = (string) FinishTimes[0];
                    second = (string) FinishTimes[1];
                    third = (string) FinishTimes[2];
                    break;
                case 5:
                    first = (string) FinishTimes[0];
                    second = (string) FinishTimes[1];
                    third = (string) FinishTimes[2];
                    break;
                case 6:
                    first = (string) FinishTimes[0];
                    second = (string) FinishTimes[1];
                    third = (string) FinishTimes[2];
                    break;
                case 7:
                    first = (string) FinishTimes[0];
                    second = (string) FinishTimes[1];
                    third = (string) FinishTimes[2];
                    break;
                case 8:
                    first = (string) FinishTimes[0];
                    second = (string) FinishTimes[1];
                    third = (string) FinishTimes[2];
                    break;
            }

            Lane_1.DisableInterrupt();
            Lane_2.DisableInterrupt();
            Lane_3.DisableInterrupt();
            Lane_4.DisableInterrupt();
            Lane_5.DisableInterrupt();
            Lane_6.DisableInterrupt();
            Lane_7.DisableInterrupt();
            Lane_8.DisableInterrupt();

            LCD.WriteText("Grand Prix Tim" +
                          "er v0.1       " +
                          "Race Complete!" +
                          "Win:  " + first.ToString() +
                          "2nd:  " + second.ToString() +
                          "3rd:  " + third.ToString() );

            Debug.Print("Race Completed");
            SendRaceTimes(false);
            Debug.Print("Sent race times");
            
            //Thread.Sleep(500);
            LCD.BacklightBrightness = 40;
        }

        static void ONBOARD_SW1_OnInterrupt(uint data1, uint data2, DateTime time)
        {
            //someone pressed the onboard switch
            Debug.Print("Onboard switch was pressed");

            // for now lest use it to print out the values of all the lane timers and "start" the race.
        }

        static void StartGate_OnInterrupt(uint CPUpin, uint State, DateTime time)
        {
            Debug.Print("Start Gate was triggered: " + time.Subtract(StartTime).Ticks.ToString());
            /*
            const int SG_OPEN = 0;
            const int SG_CLOSED = 1;
            const int LANE_1 = 2;
            const int LANE_2 = 3;
            const int LANE_3 = 4;
            const int LANE_4 = 5;
            const int LANE_5 = 6;
            const int LANE_6 = 7;
            const int LANE_7 = 8;
            const int LANE_8 = 9;
            const int SC_ERROR = 100;
             */
            // Need to Verify the State of the Port when the Gate is closed
            switch (State)
            {
            case SG_OPEN:
                    if (StatusCheck)
                    {
                        if (SC_State == SC_START)
                        {
                            // Right event was triggered
                            SC_State = SG_OPEN;
                            break;
                        }
                        else
                        {
                            // nope, the wrong event was triggered.
                            SC_State = SC_ERROR + SG_OPEN;
                            break;
                        }
                    }
                    else
                    {
                        startRace(time);
                    }
                    break;

            case SG_CLOSED:
                    if (StatusCheck)
                    {
                        if (SC_State == SG_OPEN)
                        {
                            // Right event was triggered
                            SC_State = SG_CLOSED;
                            break;
                        }
                        else
                        {
                            // nope, the wrong event was triggered.
                            SC_State = SC_ERROR + SG_CLOSED;
                            break;
                        }
                    }
                    else
                    {
                        Reset();
                    }
                    break;
            default:
                    //something bad happend... you should not be here.
                    Debug.Print("StartGate_OnInterrupt State:" + State.ToString());
                    break;
        }
        }
    }


    /*
     * The Class below comes from the users "Omer" and "AlfredBr" over on the community
     * Forums at Netduino.com. THANKS GUYS!!! See the link below for the latest information:
     * http://forums.netduino.com/index.php?/topic/860-lcd-help-needed-16x2s-are-boring
     * 
     * The version below is from Feb 1st or there about and has a couple of modifications.
     * they were only done to get the code to compile and replacement with a newer version
     * shoudl not be an issue if you want to use some of the new graphics librarys they
     * have added.
     */
    public class Nokia_5110
    {
        private bool[] _offRow = new bool[84];

        private readonly byte[] _pow = new byte[] { 1, 2, 4, 8, 16, 32, 64, 128 };

        private OutputPort _resetPort, _daComPin;
        private PWM _backlightPort;
        private SPI _dataPort;

        public bool[][] _pixelMap = new bool[48][];
        private byte[] _byteMap = new byte[504];

        private uint _backlight = 0;
        private bool _invd = false, _autoRefresh = true;

        /*
        LCD|Netduino
          1|3V3
          2|GND
          3|ChipSelect (Pin 10)
          4|Reset(Pin 7)
          5|Data/Command (Pin 8)
          6|Pin 11
          7|Pin 13
          8|Backlight (Pin 9 PWM)
         */

        public Nokia_5110(  Cpu.Pin chipSelect =    Pins.GPIO_PIN_D3, 
                            Cpu.Pin backlight =     Pins.GPIO_PIN_D9,
                            Cpu.Pin reset =         Pins.GPIO_PIN_D7, 
                            Cpu.Pin dataCommand =   Pins.GPIO_PIN_D8)
        {
            SPI.Configuration spiConfiguration = new SPI.Configuration(
                chipSelect,            // chip select port
                false,                 // IC is accessed when chip select is low
                0,                     // setup time 1 ms
                0,                     // hold chip select 1 ms after transfer
                false,                 // clock line is low if device is not selected
                true,                  // data is sampled at leading edge of clock
                2000,                 // clockrate is 15 MHz
                SPI.SPI_module.SPI1   // use first SPI bus
                );

            _dataPort = new SPI(spiConfiguration);
            
            //comment next line to free backlight pin
            _backlightPort = new PWM(backlight);
            _resetPort = new OutputPort(reset, true);
            _daComPin = new OutputPort(dataCommand, true);

            Initialize();
            Clear();
        }

        private void Initialize()
        {
            _resetPort.Write(false);
            _resetPort.Write(true);
            DataMode = false;
            _dataPort.Write(new byte[] { 0x21, 0xBF, 0x04, 0x14, 0x0C, 0x20, 0x0C });
            DataMode = true;
            Clear();
        }

        public void SetCursor(float x, float y)
        {
            DataMode = false;
            _dataPort.Write(new byte[] { (byte)(0x80 | (int)(x * 4)), (byte)(0x40 | (int)(y)) });
            DataMode = true;
        }

        public void WriteText(string text, bool noSpace = true)
        {
            DataMode = true;

            foreach (var c in text.ToCharArray())
            {
                var glyph = new byte[5];
                for (var i = 0; i < 5; i++)
                {
                    var pos = ((c - 0x20) * 5) + i;
                    try
                    {
                        glyph[i] = ASCII[pos];
                    }
                    catch
                    {
                        // we found an unsupported char
                    }
                }
                _dataPort.Write(glyph);
                _dataPort.Write(new byte[] { 0x00 });
            }

            //if (noSpace)
            //    foreach (char c in text.ToCharArray())
            //        _dataPort.Write(ASCII[c - 0x20]);
            //else
            //{
            //    byte[] letter = new byte[6];
            //    foreach (char c in text.ToCharArray())
            //    {
            //        for (int i = 0; i < 5; i++)
            //            letter[i] = ASCII[c - 0x20][i];

            //        letter[5] = 0x00;
            //        _dataPort.Write(letter);
            //    }

            //}
        }

        public void DrawRectangle(int x, int y, int width, int height, bool filled)
        {
            // thats what lines are for! oh right... I need to make a Draw line method... 
            if (width <= 1 || height <= 1)
                return;

            int endX = x + width, endY = y + height - 2;

            bool[] line = new bool[84];

            for (int p = x; p < endX; p++)
                line[p] = true;

            _pixelMap[y] = line;
            _pixelMap[endY] = line;

            if (!filled)
            {
                line = new bool[84];
                line[x] = true;
                line[endX - 1] = true;
            }

            for (int h = y + 1; h < endY; h++)
                _pixelMap[h] = line;

            if (AutoRefresh)
                Refresh();
        }

        public void Refresh()
        {
            int byteID = 0;

            for (int y = 0; y < 6; y++)
            {
                for (int x = 0; x < 84; x++)
                {
                    _byteMap[byteID] = ConvertToByte(_pixelMap[y * 8][x], _pixelMap[y * 8 + 1][x],
                                       _pixelMap[y * 8 + 2][x], _pixelMap[y * 8 + 3][x], _pixelMap[y * 8 + 4][x],
                                       _pixelMap[y * 8 + 5][x], _pixelMap[y * 8 + 6][x], _pixelMap[y * 8 + 7][x]);
                    byteID++;
                }
            }

            _dataPort.Write(_byteMap);
            SetCursor(0, 0);
        }

        private byte ConvertToByte(bool b0, bool b1, bool b2, bool b3, bool b4, bool b5, bool b6, bool b7)
        {
            byte result = 0;

            result += (byte)((b7) ? _pow[7] : 0);
            result += (byte)((b6) ? _pow[6] : 0);
            result += (byte)((b5) ? _pow[5] : 0);
            result += (byte)((b4) ? _pow[4] : 0);
            result += (byte)((b3) ? _pow[3] : 0);
            result += (byte)((b2) ? _pow[2] : 0);
            result += (byte)((b1) ? _pow[1] : 0);
            result += (byte)((b0) ? _pow[0] : 0);

            return result;
        }

        public void Clear()
        {
            for (int r = 0; r < 48; r++)
                _pixelMap[r] = _offRow;

            if (_autoRefresh)
                Refresh();
        }

        private void Inverse(bool inverse)
        {
            _invd = inverse;
            DataMode = false;
            _dataPort.Write(inverse ? new byte[] { 0x0D } : new byte[] { 0x0C });
            DataMode = true;
        }

        public bool InverseColors
        {
            get { return _invd; }
            set { Inverse(value); }
        }

        private bool DataMode
        {
            get { return _daComPin.Read(); }
            set { _daComPin.Write(value); }
        }

        public uint BacklightBrightness
        {
            get
            { return _backlight; }
            set
            {
                if (value > 100)
                    value = 100;

                _backlightPort.SetDutyCycle(value);
                _backlight = 100;
            }
        }

        public bool AutoRefresh
        {
            get { return _autoRefresh; }
            set { _autoRefresh = value; }
        }
        #region ASCII Chars

        private static readonly byte[] ASCII = new byte[]
                                           {
                                               0x00, 0x00, 0x00, 0x00, 0x00 // 20  
                                               , 0x00, 0x00, 0x5f, 0x00, 0x00 // 21 !
                                               , 0x00, 0x07, 0x00, 0x07, 0x00 // 22 "
                                               , 0x14, 0x7f, 0x14, 0x7f, 0x14 // 23 #
                                               , 0x24, 0x2a, 0x7f, 0x2a, 0x12 // 24 $
                                               , 0x23, 0x13, 0x08, 0x64, 0x62 // 25 %
                                               , 0x36, 0x49, 0x55, 0x22, 0x50 // 26 &
                                               , 0x00, 0x05, 0x03, 0x00, 0x00 // 27 '
                                               , 0x00, 0x1c, 0x22, 0x41, 0x00 // 28 (
                                               , 0x00, 0x41, 0x22, 0x1c, 0x00 // 29 )
                                               , 0x14, 0x08, 0x3e, 0x08, 0x14 // 2a *
                                               , 0x08, 0x08, 0x3e, 0x08, 0x08 // 2b +
                                               , 0x00, 0x50, 0x30, 0x00, 0x00 // 2c ,
                                               , 0x08, 0x08, 0x08, 0x08, 0x08 // 2d -
                                               , 0x00, 0x60, 0x60, 0x00, 0x00 // 2e .
                                               , 0x20, 0x10, 0x08, 0x04, 0x02 // 2f /
                                               , 0x3e, 0x51, 0x49, 0x45, 0x3e // 30 0
                                               , 0x00, 0x42, 0x7f, 0x40, 0x00 // 31 1
                                               , 0x42, 0x61, 0x51, 0x49, 0x46 // 32 2
                                               , 0x21, 0x41, 0x45, 0x4b, 0x31 // 33 3
                                               , 0x18, 0x14, 0x12, 0x7f, 0x10 // 34 4
                                               , 0x27, 0x45, 0x45, 0x45, 0x39 // 35 5
                                               , 0x3c, 0x4a, 0x49, 0x49, 0x30 // 36 6
                                               , 0x01, 0x71, 0x09, 0x05, 0x03 // 37 7
                                               , 0x36, 0x49, 0x49, 0x49, 0x36 // 38 8
                                               , 0x06, 0x49, 0x49, 0x29, 0x1e // 39 9
                                               , 0x00, 0x36, 0x36, 0x00, 0x00 // 3a :
                                               , 0x00, 0x56, 0x36, 0x00, 0x00 // 3b ;
                                               , 0x08, 0x14, 0x22, 0x41, 0x00 // 3c <
                                               , 0x14, 0x14, 0x14, 0x14, 0x14 // 3d =
                                               , 0x00, 0x41, 0x22, 0x14, 0x08 // 3e >
                                               , 0x02, 0x01, 0x51, 0x09, 0x06 // 3f ?
                                               , 0x32, 0x49, 0x79, 0x41, 0x3e // 40 @
                                               , 0x7e, 0x11, 0x11, 0x11, 0x7e // 41 A
                                               , 0x7f, 0x49, 0x49, 0x49, 0x36 // 42 B
                                               , 0x3e, 0x41, 0x41, 0x41, 0x22 // 43 C
                                               , 0x7f, 0x41, 0x41, 0x22, 0x1c // 44 D
                                               , 0x7f, 0x49, 0x49, 0x49, 0x41 // 45 E
                                               , 0x7f, 0x09, 0x09, 0x09, 0x01 // 46 F
                                               , 0x3e, 0x41, 0x49, 0x49, 0x7a // 47 G
                                               , 0x7f, 0x08, 0x08, 0x08, 0x7f // 48 H
                                               , 0x00, 0x41, 0x7f, 0x41, 0x00 // 49 I
                                               , 0x20, 0x40, 0x41, 0x3f, 0x01 // 4a J
                                               , 0x7f, 0x08, 0x14, 0x22, 0x41 // 4b K
                                               , 0x7f, 0x40, 0x40, 0x40, 0x40 // 4c L
                                               , 0x7f, 0x02, 0x0c, 0x02, 0x7f // 4d M
                                               , 0x7f, 0x04, 0x08, 0x10, 0x7f // 4e N
                                               , 0x3e, 0x41, 0x41, 0x41, 0x3e // 4f O
                                               , 0x7f, 0x09, 0x09, 0x09, 0x06 // 50 P
                                               , 0x3e, 0x41, 0x51, 0x21, 0x5e // 51 Q
                                               , 0x7f, 0x09, 0x19, 0x29, 0x46 // 52 R
                                               , 0x46, 0x49, 0x49, 0x49, 0x31 // 53 S
                                               , 0x01, 0x01, 0x7f, 0x01, 0x01 // 54 T
                                               , 0x3f, 0x40, 0x40, 0x40, 0x3f // 55 U
                                               , 0x1f, 0x20, 0x40, 0x20, 0x1f // 56 V
                                               , 0x3f, 0x40, 0x38, 0x40, 0x3f // 57 W
                                               , 0x63, 0x14, 0x08, 0x14, 0x63 // 58 X
                                               , 0x07, 0x08, 0x70, 0x08, 0x07 // 59 Y
                                               , 0x61, 0x51, 0x49, 0x45, 0x43 // 5a Z
                                               , 0x00, 0x7f, 0x41, 0x41, 0x00 // 5b [
                                               , 0x02, 0x04, 0x08, 0x10, 0x20 // 5c ¥
                                               , 0x00, 0x41, 0x41, 0x7f, 0x00 // 5d ]
                                               , 0x04, 0x02, 0x01, 0x02, 0x04 // 5e ^
                                               , 0x40, 0x40, 0x40, 0x40, 0x40 // 5f _
                                               , 0x00, 0x01, 0x02, 0x04, 0x00 // 60 `
                                               , 0x20, 0x54, 0x54, 0x54, 0x78 // 61 a
                                               , 0x7f, 0x48, 0x44, 0x44, 0x38 // 62 b
                                               , 0x38, 0x44, 0x44, 0x44, 0x20 // 63 c
                                               , 0x38, 0x44, 0x44, 0x48, 0x7f // 64 d
                                               , 0x38, 0x54, 0x54, 0x54, 0x18 // 65 e
                                               , 0x08, 0x7e, 0x09, 0x01, 0x02 // 66 f
                                               , 0x0c, 0x52, 0x52, 0x52, 0x3e // 67 g
                                               , 0x7f, 0x08, 0x04, 0x04, 0x78 // 68 h
                                               , 0x00, 0x44, 0x7d, 0x40, 0x00 // 69 i
                                               , 0x20, 0x40, 0x44, 0x3d, 0x00 // 6a j 
                                               , 0x7f, 0x10, 0x28, 0x44, 0x00 // 6b k
                                               , 0x00, 0x41, 0x7f, 0x40, 0x00 // 6c l
                                               , 0x7c, 0x04, 0x18, 0x04, 0x78 // 6d m
                                               , 0x7c, 0x08, 0x04, 0x04, 0x78 // 6e n
                                               , 0x38, 0x44, 0x44, 0x44, 0x38 // 6f o
                                               , 0x7c, 0x14, 0x14, 0x14, 0x08 // 70 p
                                               , 0x08, 0x14, 0x14, 0x18, 0x7c // 71 q
                                               , 0x7c, 0x08, 0x04, 0x04, 0x08 // 72 r
                                               , 0x48, 0x54, 0x54, 0x54, 0x20 // 73 s
                                               , 0x04, 0x3f, 0x44, 0x40, 0x20 // 74 t
                                               , 0x3c, 0x40, 0x40, 0x20, 0x7c // 75 u
                                               , 0x1c, 0x20, 0x40, 0x20, 0x1c // 76 v
                                               , 0x3c, 0x40, 0x30, 0x40, 0x3c // 77 w
                                               , 0x44, 0x28, 0x10, 0x28, 0x44 // 78 x
                                               , 0x0c, 0x50, 0x50, 0x50, 0x3c // 79 y
                                               , 0x44, 0x64, 0x54, 0x4c, 0x44 // 7a z
                                               , 0x00, 0x08, 0x36, 0x41, 0x00 // 7b 
                                               , 0x00, 0x00, 0x7f, 0x00, 0x00 // 7c |
                                               , 0x00, 0x41, 0x36, 0x08, 0x00 // 7d 
                                               , 0x10, 0x08, 0x08, 0x10, 0x08 // 7e ←
                                               , 0x78, 0x46, 0x41, 0x46, 0x78 // 7f →
                                           };

        #endregion
    }
}
